////////////////////////////////////////////////////////////////
//
//       Filename:  Doctor.go
//
//        Version:  1.0
//        Created:  2022年11月08日 16时07分33秒
//       Revision:  none
//       Compiler:  go
//
//         Author:  alpha
//   Organization:  alpha
//       Contacts:  chenxinquan@kylinos.com
//
////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////
// Description:
////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////
// Log:
////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////
// Todo:
//
////////////////////////////////////////////////////////////////

package genmai

import (
    "fmt"
    "os"
    "sync"
    "time"
    "github.com/mitchellh/mapstructure"
    "github.com/google/uuid"
    sandbox "main/genmai/Sandbox"
)

const (
    EXP_TYPE_DBus       = "dbus"
    EXP_TYPE_KERNEL     = "kernel"
    EXP_TYPE_SYSTEM     = "system"
    EXP_TYPE_WEB        = "web"
    EXP_TYPE_BASELINE   = "baseline"
)

type
doctor struct {
    ExplorersDBus       map[string]ExplorerDBus
    ExplorersKernel     map[string]ExplorerKernel
    ExplorersSystem     map[string]ExplorerSystem
    ExplorersWeb        map[string]ExplorerWeb
    ExplorersBaseLine   map[string]ExplorerBaseLine
}


type
ExplorerItem struct {
    ConfigFile          string

}
type
ExplorersListConfig struct {
    ConfigFilePrefix    string
    RootPasswd          string
    Type                string
    ExplorerItems       []ExplorerItem
}

///////////////////////////////
// Singleton
//type Singleton interface {
//}

var (
    instance    *doctor
    once        sync.Once
)

func
DoctorIns() (*doctor) {
    once.Do( func() {
        instance = &doctor{}
        // go is fuck,
        // it will cause panic: assignment to entry in nil map
        // without this
        instance.ExplorersKernel =   make(map[string]ExplorerKernel)
        instance.ExplorersBaseLine = make(map[string]ExplorerBaseLine)
    }) // once.Do ...

    return instance
}

///////////////////////////////
// doctor functions
func
(dtr *doctor)Reset() {
    // 清空所有map元素
    dtr.ExplorersDBus   = make(map[string]ExplorerDBus)
    dtr.ExplorersKernel = make(map[string]ExplorerKernel)
    dtr.ExplorersSystem = make(map[string]ExplorerSystem)
    dtr.ExplorersWeb    = make(map[string]ExplorerWeb)
    dtr.ExplorersBaseLine = make(map[string]ExplorerBaseLine)
}

func
(dtr *doctor)Genmai() (rps []ReportBase) {

    // dtr.GenmaiBaseline()
    // dtr.GenmaiKernel()
    return rps
    
    // var wg sync.WaitGroup
    // wg.Add(1) // task 数必须正确

    // lock_rps := sync.Mutex{}

    // // task 1
    // go func() {
    //     defer wg.Done()

    //     rp := dtr.GenmaiKernel()

    //     lock_rps.Lock()
    //     defer lock_rps.Unlock()
    //     rps = append(rps, rp)
    // } ()

    // // task 2
    // go func () {
    //     defer wg.Done()

    //     rp := dtr.GenmaiSystem()

    //     lock_rps.Lock()
    //     defer lock_rps.Unlock()
    //     rps = append(rps, rp)

    // } ()

    // wg.Wait()

    // return rps
}

func
(dtr *doctor)GenmaiBaseline() (rp *ReportKernel) {
    for _, v := range dtr.ExplorersBaseLine {
            expvul, rc := v.Explore()
            // TODO: is append expvul to expvuls according to rc
            if nil != rc {
            }


           // expvuls = append(expvuls, expvul)
    if len(expvul.VICId)>0{
        infoExist:="Baseline info: "+expvul.VICId+" exists"
        fmt.Printf("%c[%d;%d;%dm%s%c[0m\n", 0x1B, 0, 0, 32, infoExist, 0x1B)
    }
    } // for _, v ...
    return rp
}
func
(dtr *doctor)GenmaiKernel() (rp *ReportKernel) {
    rp = GetTemplateReportKernel()

    var expip4addr []string
    expip4addr = append(expip4addr, "0.0.0.0")
    var expip6addr []string
    expip6addr = append(expip6addr, "0.0.0.0")
    /* */
    rp.RCServerUUID        = uuid.New().String()
    rp.RCServerName, _     = os.Hostname()
    rp.RCFamily            = "RCFamily"
    rp.RCRelease           = "RCRelease"
    rp.RCContainer         = "RCContainer"
    /* */
    rp.RCExploredType      = "Kernel"
    rp.RCExploredTimeAt    = time.Now()
    rp.RCExploredMode      = "RCExploredMode"
    rp.RCExploredVersion   = "RCExploredVersion"
    rp.RCExploredRevision  = "RCExploredRevision"
    rp.RCExploredBy        = "RCExploredBy"
    rp.RCExploredVia       = "RCExploredVia"
    rp.RCExploredIPv4Addrs = expip4addr
    rp.RCExploredIPv6Addrs = expip6addr
    /* */
    rp.RCReportedAt        = time.Now()
    rp.RCReportedVersion   = "RCReportedVersion"
    rp.RCReportedBy        = "RCReportedBy"
    /* */
    rp.RCErrors            = "RCErrors"
    rp.RCWarnings          = "RCWarnings"

    rp.RCReunningKernelInfo = ReportKernelInfo {
                            "0.0",
                            "0.0",
                            false,
                        }
    rp.RCPackages          = "RCPackages"
    rp.RCSrcPackages       = "RCSrcPackages"
    rp.RCOptional          = "RCOptional"

    var wg sync.WaitGroup
    wg.Add(len(dtr.ExplorersKernel))
    /* */
    lock_expvuls := sync.Mutex{}
    var expvuls []VulnInfoCommon
    /* */
    for _, v := range dtr.ExplorersKernel {
        go func(v_ ExplorerKernel) {
            defer wg.Done()

            expvul, rc := v_.Explore()

            // TODO: is append expvul to expvuls according to rc
            if nil != rc {
            }

            lock_expvuls.Lock()
            defer lock_expvuls.Unlock()
            expvuls = append(expvuls, expvul)
        } (v)

    } // for _, v ...
    /* */
    wg.Wait()

    // TODO: should be type: ConfigCommon..
    rp.RCExploredVulns      = expvuls
    /* */
    rp.RCReportedAt         = time.Now()
    rp.RCElapsedTime        = time.Since(rp.RCExploredTimeAt)

    return rp
}

func
(dtr *doctor)GenmaiSystem() (rp *ReportSystem) {
    rp = GetTemplateReportSystem()

    var expip4addr []string
    expip4addr = append(expip4addr, "0.0.0.0")
    var expip6addr []string
    expip6addr = append(expip6addr, "0.0.0.0")
    /* */
    rp.RCServerUUID        = uuid.New().String()
    rp.RCServerName, _     = os.Hostname()
    rp.RCFamily            = "RCFamily"
    rp.RCRelease           = "RCRelease"
    rp.RCContainer         = "RCContainer"
    /* */
    rp.RCExploredType      = "System"
    rp.RCExploredTimeAt    = time.Now()
    rp.RCExploredMode      = "RCExploredMode"
    rp.RCExploredVersion   = "RCExploredVersion"
    rp.RCExploredRevision  = "RCExploredRevision"
    rp.RCExploredBy        = "RCExploredBy"
    rp.RCExploredVia       = "RCExploredVia"
    rp.RCExploredIPv4Addrs = expip4addr
    rp.RCExploredIPv6Addrs = expip6addr
    /* */
    rp.RCReportedAt        = time.Now()
    rp.RCReportedVersion   = "RCReportedVersion"
    rp.RCReportedBy        = "RCReportedBy"
    /* */
    rp.RCErrors            = "RCErrors"
    rp.RCWarnings          = "RCWarnings"

    rp.RCReunningKernelInfo = ReportKernelInfo {
                            "0.0",
                            "0.0",
                            false,
                        }
    rp.RCPackages          = "RCPackages"
    rp.RCSrcPackages       = "RCSrcPackages"
    rp.RCOptional          = "RCOptional"

    var wg sync.WaitGroup
    wg.Add(len(dtr.ExplorersSystem))
    /* */
    lock_expvuls  := sync.Mutex{}
    var expvuls []VulnInfoCommon
    for _, v := range dtr.ExplorersSystem {
        go func(v_ ExplorerSystem) {
            defer wg.Done()

            expvul, rc := v_.Explore()

            // TODO: is append expvul to expvuls according to rc
            if nil != rc {
            }

            lock_expvuls.Lock()
            defer lock_expvuls.Unlock()
            expvuls = append(expvuls, expvul)
        } (v)

    } // for _, v ...
    /* */
    wg.Wait()

    // TODO: should be type: ConfigCommon..
    rp.RCExploredVulns      = expvuls
    /* */
    rp.RCReportedAt         = time.Now()
    rp.RCElapsedTime        = time.Since(rp.RCExploredTimeAt)

    return rp
}

func
(dtr *doctor)LoadExplorersListConfig(configf string) (rc error) {
    var parser ConfigParserBase

    filetype := GetSubfixFile(configf)
    /* */
    if  ( (".yml"  == filetype) ||
          (".yaml" == filetype)      ) {
        A_DEBUG_INFO("Got a YAML:", configf)
        /* */
        parser = &ConfigParserYAML{}
    } else if ( (".json" == filetype) ) {
        A_DEBUG_INFO("Got a YAML:", configf)
        /* */
        parser = &ConfigParserJSON{}
    } else {
        A_DEBUG_WARNING("Can not determine WHAT format is! file:", configf)
        parser = &ConfigParserDefault{}
    }


    parser.Mashal(configf)

    elc := ExplorersListConfig{}
    rc = mapstructure.Decode(parser, &elc)
    /* */
    if (nil != rc) {
        A_DEBUG_ERROR(rc)
        /* */
        return rc
    }


        switch (elc.Type) {
            case EXP_TYPE_DBus:
                // dtr.PushExplorerDBus(exp.ConfigFile)
            case EXP_TYPE_KERNEL:
                for _, exp := range elc.ExplorerItems {
                    dtr.PushExplorerKernel(elc.ConfigFilePrefix + exp.ConfigFile)
                } // for exp ...
            case EXP_TYPE_SYSTEM:
                for _, exp := range elc.ExplorerItems {
                    dtr.PushExplorerSystem(elc.ConfigFilePrefix + exp.ConfigFile)
                } // for exp ...
            case EXP_TYPE_WEB:
                // dtr.PushExplorerWeb(exp.ConfigFile)
            case EXP_TYPE_BASELINE:
                for _, exp := range elc.ExplorerItems {
                    dtr.PushExplorerBaseLine(elc.ConfigFilePrefix + exp.ConfigFile, elc.RootPasswd)
                } // for exp ...
            default:
                A_DEBUG_ERROR("Unknow exp type!!")
        } // switch (exp.Type ...


    return rc
}

func
(dtr *doctor)PushExplorerDBus(configfile string) error {
//    // TODO: need to test
//    ed := ExplorerDBus{}
//    ed.Setup(&ConfigParserYAML{}, &ExplorerConfigDBus{})
//    ed.LoadConfig(configfile)
//
//    ed.SetupSandbox(&sandbox.SandboxDefault{})
//
//    config, rc := ed.GetExplorerConfigDBus()
//    /* */
//    if (nil != rc) {
//        return rc
//    } // if (nil != ...
//
//    dtr.ExplorersDBus[config.Id] = ed

    return nil
}

func
(dtr *doctor)PushExplorerKernel(configfile string) error {
    // TODO: need to test
    ek := ExplorerKernel{}
    ek.Setup(&ConfigParserYAML{}, &ExplorerConfigKernel{})
    ek.LoadConfig(configfile)

    ek.SetupSandbox(&sandbox.SandboxDefault{})

    config, rc := ek.GetExplorerConfigKernel()
    /* */
    if (nil != rc) {
        return rc
    } // if (nil != ...

    dtr.ExplorersKernel[config.Id] = ek

    return nil
}

func
(dtr *doctor)PushExplorerSystem(configfile string) error {
    // TODO: need to test
    ek := ExplorerSystem{}
    ek.Setup(&ConfigParserYAML{}, &ExplorerConfigSystem{})
    ek.LoadConfig(configfile)

    ek.SetupSandbox(&sandbox.SandboxDefault{})

    config, rc := ek.GetExplorerConfigSystem()
    /* */
    if (nil != rc) {
        return rc
    } // if (nil != ...
    dtr.ExplorersSystem[config.Id] = ek

    return nil
}

func
(dtr *doctor)PushExplorerWeb(configfile string) error {
//    // TODO: need to test
//    ew := ExplorerWeb{}
//    ew.Setup(&ConfigParserYAML{}, &ExplorerConfigWeb{})
//    ew.LoadConfig(configfile)
//
//    ew.SetupSandbox(&sandbox.SandboxDefault{})
//
//    config, rc := ew.GetExplorerConfigWeb()
//    /* */
//    if (nil != rc) {
//        return rc
//    } // if (nil != ...
//
//    dtr.ExplorersWeb[config.Id] = ew

    return nil
}

func
(dtr *doctor)PushExplorerBaseLine(configfile string, passwd string) error {
    // TODO: need to test
    eb := ExplorerBaseLine{}
    eb.Setup(&ConfigParserYAML{}, &ExplorerConfigBaseLine{})
    eb.LoadConfig(configfile)
    config, rc := eb.GetExplorerConfigBaseLine()
    /* */
    if (nil != rc) {
        return rc
    } // if (nil != ...

    eb.Passwd = passwd
    dtr.ExplorersBaseLine[config.Id] = eb
    return nil
}
